package net.homeip.jcperera.nu.core.utils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;

import net.homeip.jcperera.nu.core.CoreBase;
import net.homeip.jcperera.nu.core.msg.BaseMessage;
import net.homeip.jcperera.nu.core.msg.ErrorMessage;

/**
 * Provides utility functions related to collections manipulation
 * 
 * @name Colletions Framework Utility class
 * @author julio.c.perera@gmail.com
 * @application nu
 * @since 0.1.0.0
 */

public class CollectionsUtil extends CoreBase implements Serializable
{
    // Required to implement Serializable interface
    private static final long  serialVersionUID = -6197398997003357061L;

    // CoreBase properties, for easy use from other components, as well as logging
    public static final int    runtimeLevel     = 0;
    public static final String moduleName       = "core";
    public static final String componentName    = "nu_core_utils_collections";

    /**
     * <p>
     * Builds a {@link Hashtable} with keys of type {@link String} and the provided {@link Objects} as elements.
     * </p>
     * <p>
     * The key is assigned from the value of the field specified by the parameter <code>propertyName</code> via the
     * corresponding {@link Object} in the {@link Collection}
     * </p>
     * <p>
     * You should use a field that does not repeat its value, or the last {@link Object} to assign to the previous
     * similar value will overwrite the pre-existing {@link Object} and the size of the two {@link Collection}s will not
     * match, note that there is no criteria for ordering the {@link Object}s inside the {@link Collection}
     * </p>
     * <p>
     * <b>Note:</b> If the field in the {@link Object} to use as key is not itself of type {@link String}, the resulting
     * value of the key will be converted to {@link String} using {@link toString()} and this value will be used as the
     * key instead of the primitive type of the value of the field
     * </p>
     * 
     * @param collection
     *            The {@link Collection} of {@link Object}s to be converted
     * @param propertyName
     *            The name of the <code>Property</code> in the {@link Object} to use as key value
     * @return the equivalent Hashtable
     * @throws BaseMessage
     *             in case any error occurs (e.g: No such field exists in the Objects)
     */
    public static Hashtable<String, ?> buildHashtableFromCollectionUsingPropertyAsKey(Collection<?> collection,
            String propertyName) throws BaseMessage
    {
        Hashtable<String, Object> result = new Hashtable<String, Object>();
        try
        {
            Iterator<?> iterator = collection.iterator();
            while (iterator.hasNext())
            {
                Object object = iterator.next();
                Field field;
                String key;
                Method method;
                try
                {
                    // First, try to find a public field with the provided name
                    field = object.getClass().getField(propertyName);
                    key = field.get(object).toString();
                }
                catch (Exception exception)
                {
                    // But if no such field is found or the field is not public,
                    // try to use a getter... if this fails, we are hopeless
                    propertyName = propertyName.trim();
                    char firstChar = propertyName.toUpperCase().charAt(0);
                    propertyName = firstChar + propertyName.substring(1);
                    method = object.getClass().getMethod("get" + propertyName);
                    key = (String) method.invoke(object);
                }
                result.put(key, object);
            }
        }
        catch (Exception exception)
        {
            Hashtable<String, String> extensions = new Hashtable<String, String>();
            extensions.put("%description%", exception.getClass().getName() + "=" + exception.getLocalizedMessage());
            throw new ErrorMessage("nu.system.util.collection.to.hashtable.error", BaseMessage.FRIENDLINESS_TECHNICAL,
                    extensions);
        }
        return result;
    }
}
